#!/usr/bin/env python
"""
irccat.py - jsonbot "irccat" Module
Copyright 2011, Richard Bateman
Licensed under the New BSD License.

Written to be used in the #firebreath IRC channel: http://www.firebreath.org

To test, set up the host and port, then use something like:

echo "@taxilian I am awesome" | netcat -g0 localhost 54321

echo "#channel I am awesome" | netcat -g0 localhost 54321

you can specify multiple users (with @) and channels (with #) by seperating them
with commas.  Not that with jabber, channels tend to be treated as users
unless you set up an alias in your channel:

!irccat_add_alias #channel

"""

## jsb imports

from jsb.lib.threads import start_new_thread
from jsb.lib.persist import PlugPersist
from jsb.lib.fleet import getfleet
from jsb.lib.commands import cmnds
from jsb.lib.examples import examples
from jsb.lib.callbacks import callbacks
from jsb.lib.threadloop import ThreadLoop

## basic imports

import logging
import time
import SocketServer
from SocketServer import ThreadingMixIn, StreamRequestHandler

## defines

cfg = PlugPersist("irccat", {
    "botnames": ["default-sxmpp",],
    "host": "localhost",
    "port": "54321",
    "aliases": {},
    "enable": False,
    })


shared_data = {}
server = None
outputthread = None

## irccat_output function

def irccat_output(msg):
    fleet = getfleet()
    dest, msg = splitMsg(msg)
    for chan in dest:
        logging.info("sending to %s" % chan)
        for botname in fleet.list():
            if botname not in cfg.data['botnames']: continue
            bot = fleet.byname(botname)
            if bot:
                if chan[0] == "#" and chan not in bot.state['joinedchannels']:
                    logging.warn("someone tried to make me say something in a channel i'm not in! (%s - %s)" % (bot.cfg.name, chan))
                else:
                    bot.say(chan, msg)
            else: logging.error("can't find %s bot in fleet" % botname)

## splitMsg function

def splitMsg(message):
    if message[0] not in ('#', '@'): return [], message
    dest, message = message.split(" ", 1)
    dest = [x.strip().strip("@") for x in dest.split(",")]
    finalDest = []
    for d in dest:
        finalDest.append(d)
        if d in cfg.data["aliases"].keys():
            for alias in cfg.data["aliases"][d]:
                finalDest.append(alias)
    return finalDest, message

## IrcCatOutputThread

class IrcCatOutputThread(ThreadLoop):

    def handle(self, msg):
        irccat_output(msg)

## IrcCatListener class

class IrcCatListener(ThreadingMixIn, StreamRequestHandler):

    def handle(self):
        msg = self.rfile.readline().strip()
        logging.warn("received %s" % msg)
        if outputthread and msg: outputthread.put(5, msg)        

## dummy callbacks to make sure plugin gets loaded on startup

def dummycb(bot, event): pass

callbacks.add("START", dummycb)

## plugin initialisation

def init_threaded():
    global server
    global outputthread
    if server: logging.warn("irccat server is already running.") ; return
    if not cfg.data.enable: logging.warn("irccat is not enabled.") ; return 
    time.sleep(3)
    if "host" not in cfg.data or "port" not in cfg.data:
        cfg.data["host"] = "localhost"
        cfg.data["port"] = 54321
        cfg.data["botnames"] = ["default-sxmpp",]
        cfg.data["aliases"] = {}
    if not cfg.data.aliases: cfg.data.aliases = {}
    cfg.save()
    try:
        server = SocketServer.TCPServer((cfg.data["host"], int(cfg.data["port"])), IrcCatListener)
    except Exception, ex: logging.error(str(ex)) ; return
    logging.warn("starting irccat server on %s:%s" % (cfg.data["host"], cfg.data["port"]))
    start_new_thread(server.serve_forever, ())
    outputthread = IrcCatOutputThread()
    outputthread.start()

## plugin shutdonw

def shutdown():
    global server
    if server:
        logging.warn("shutting down the irccat server")
        server.shutdown()
    if outputthread: outputthread.stop()

## irccat_add_alias command

def handle_irccat_add_alias(bot, ievent):
    if len(ievent.args) != 1:
        ievent.reply("syntax: irccat_add_alias <alias> (where <alias> is the channel you want notifications for)")
        return
    dest = ievent.args[0]
    if not cfg.data.aliases: cfg.data.aliases = {}
    if dest not in cfg.data["aliases"]:
        cfg.data["aliases"][dest] = []
    if ievent.channel not in cfg.data["aliases"][dest]:
        cfg.data["aliases"][dest].append(ievent.channel)
    cfg.save()
    ievent.reply("%s will now receive irccat messages directed at %s" % (ievent.channel, dest))
cmnds.add("irccat_add_alias", handle_irccat_add_alias, ['OPER'])
examples.add("irccat_add_alias", "add an alias to the current channel from the specified one", "irccat_add_alias #firebreath")

## irccat_list_aliases command

def handle_irccat_list_aliases(bot, ievent):
    """ List all aliases defined for the current channel """
    aliases = [dest for dest, chanlist in cfg.data["aliases"].iteritems() if ievent.channel in chanlist]

    ievent.reply("%s is receiving irccat messages directed at: %s" % (ievent.channel, ", ".join(aliases)))
cmnds.add("irccat_list_aliases", handle_irccat_list_aliases, ['OPER'])
examples.add("irccat_list_aliases", "lists the aliases for the current channel", "irccat_list_aliases")

## irccat_del_alias command

def handle_irccat_del_alias(bot, ievent):
    if len(ievent.args) != 1:
        ievent.reply("syntax: irccat_del_alias <alias> (where <alias> is the channel you no longer want notifications for)")
        return
    dest = ievent.args[0]
    if dest not in cfg.data["aliases"]or ievent.channel not in cfg.data["aliases"][dest]:
        ievent.reply("%s is not an alias for %s" % (ievent.channel, dest))
        return

    cfg.data["aliases"][dest].remove(ievent.channel)
    ievent.reply("%s will no longer receive irccat messages directed at %s" % (ievent.channel, dest))
    cfg.save()
cmnds.add("irccat_del_alias", handle_irccat_del_alias, ['OPER'])
examples.add("irccat_del_alias", "add an alias to the current channel from the specified one", "irccat_del_alias #firebreath")

## ircccat_enable command

def handle_irccat_enable(bot, event):
    cfg.data.enable = True ; cfg.save() ; event.done()
    init_threaded()
    
cmnds.add("irccat-enable", handle_irccat_enable, "OPER")
examples.add("irccat-enable", "enable irccat server", "irccat-enable")

## irccat_disable command

def handle_irccat_disable(bot, event):
    cfg.data.enable = False ; cfg.save() ; event.done()
    shutdown()
    
cmnds.add("irccat-disable", handle_irccat_disable, "OPER")
examples.add("irccat-disable", "disable irccat server", "irccat-disable")
